﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace Luminal
{
    struct ErrorCase
    {
        public ErrorCode ErrorCode;
        public Exception AlternateException;

        public ErrorCase(ErrorCode errorCode, Exception alternateException)
        {
            ErrorCode = errorCode;
            AlternateException = alternateException;
        }
    }

    delegate void PinFunction(IntPtr ptr);

    static class Util
    {
        public static void Pin<T>(T obj, PinFunction func) where T : class
        {
            GCHandle handle = GCHandle.Alloc(obj, GCHandleType.Pinned);
            func(handle.AddrOfPinnedObject());
            handle.Free();
        }

        public static void Pin<T>(ref T obj, PinFunction func) where T : struct
        {
            Type type = typeof(T);
            IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(type));
            Marshal.StructureToPtr(obj, ptr, true);
            func(ptr);
            obj = (T)Marshal.PtrToStructure(ptr, type);
        }

        public static void Pin(ref string str, PinFunction func)
        {
            IntPtr ptr = Marshal.StringToHGlobalAnsi(str);
            func(ptr);
            str = Marshal.PtrToStringAnsi(ptr);
        }

        public static GCHandle Pin(this object obj)
        {
            return GCHandle.Alloc(obj, GCHandleType.Pinned);
        }

        public static IntPtr[] ExtractPointers<T>(IEnumerable<T> objects) where T : LuminalObject
        {
            List<IntPtr> output = new List<IntPtr>();

            IEnumerator<T> enumerator = objects.GetEnumerator();
            enumerator.Reset();
            while (enumerator.MoveNext())
                output.Add(enumerator.Current.InternalPointer);

            return output.ToArray();
        }

        public static int[] ToIntArray(this SizeT[] input)
        {
            int[] output = new int[input.Length];
            for (int i = 0; i < output.Length; i++)
                output[i] = input[i];
            return output;
        }

        public static void ThrowError(string description, ErrorCode error, params ErrorCase[] alternateMessages)
        {
            foreach (ErrorCase alternate in alternateMessages)
                if (error == alternate.ErrorCode)
                    throw alternate.AlternateException;

            if (error == ErrorCode.CL_OUT_OF_HOST_MEMORY)
                throw new OutOfMemoryException();

            if (error != ErrorCode.CL_SUCCESS)
                throw new LuminalException(string.Format("{0} Error Code: {1} ({2})", description, (int)error, error));
        }
    }
}
